package com.app.framework.socket.client;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;

import com.app.framework.exception.CommunicationException;
import com.app.framework.handler.GameUpstreamHandler;
import com.app.framework.pipelinefactory.SocketPipelineFactory;
import com.app.framework.socket.Packet;
import com.app.framework.socket.msg.MessageType;
import com.app.framework.socket.msg.SystemProtoBuffer;
import com.app.framework.socket.sender.Server;

/**
 * 服务器与服务器之间的通信
 * @author lisong_otd
 *
 */
public class ServerClient {

	private static final Logger logger = Logger.getLogger(ServerClient.class);
	
	private String ip;
	
	private int port;
	
	private ClientBootstrap bootstrap;
	
	private Server sender;
	
	private int serverId;
	
	private int serverType;
	
	public static Semaphore semaphore = new Semaphore(0);
	
	private ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
	
	public ServerClient(String ip,int port,int serverId,int serverType,Executor executor,GameUpstreamHandler handler) {
		
		this.ip = ip;
		
		this.port = port;
		
		this.serverId = serverId;
		
		this.serverType = serverType;
		
		if(port == 0) {
			logger.error("未配置world服务器");
			throw new CommunicationException("未配置world服务器");
		}
		
		bootstrap= new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool()));
		
		SocketPipelineFactory factory = new SocketPipelineFactory(executor,handler);
		
		bootstrap.setPipelineFactory(factory);

		bootstrap.setOption("child.tcpNoDelay", true);//child的属性 是与客户端建立连接的socket的,非child的是服务器负责监听客户端连接的主serversocket的
		
		bootstrap.setOption("child.soLinger", 1);
		
		getSender();
		
		service.scheduleWithFixedDelay(new Runnable() {
			@Override
			public void run() {
				getSender();
			}
		}, 0, 10, TimeUnit.SECONDS);
	}
	
	private Channel connect() {
		try {
			ChannelFuture cf = bootstrap.connect(new InetSocketAddress(ip,port));
			cf.awaitUninterruptibly();
			if(cf.isSuccess()) {
				Channel channel = cf.getChannel();
				logger.info("connected world server.......");
				return channel;
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("连接远程主机失败",e);
		}
		return null;
	}

	public Server getSender() {
		if(activeSender(sender)) {
			return sender;
		}
		synchronized (this) {
			try {
				if(activeSender(sender)) {
					return sender;
				}
				Channel tmp = connect();
				semaphore.acquire();
				Server sender = (Server) tmp.getAttachment();
				if(activeSender(sender)) {
					register(sender);
					this.sender = sender;
					return sender;
				}
			} catch (Exception e) {
				e.printStackTrace();
			} 
		}
//		synchronized (ServerClient.class) {
//			if(activeSender(sender)) {
//				return sender;
//			}
//			Channel tmp = connect();
//			System.out.println("connect:"+tmp);
//			Server sender = (Server) tmp.getAttachment();
//			System.out.println("channel:"+tmp+" atta:"+sender);
//			if(activeSender(sender)) {
//				register(sender);
//				this.sender = sender;
//				System.out.println("sender:"+this.sender.getChannel());
//				return sender;
//			}
//		}
		return null;
	}
	
	private void register(Server sender) {
		SystemProtoBuffer.Register.Builder b = SystemProtoBuffer.Register.newBuilder();
		b.setServerId(serverId);
		b.setServerType(serverType);
		Packet p = new Packet(MessageType.REGISTRY, b);
		sender.setId(serverId);
		sender.setType(serverType);
		sender.getChannel().write(p);
	}
	
	public void sendMessage(Packet... packet) {
		getSender().sendMessage(packet);
	}
	
	public void sendMessage(List<Packet> packet) {
		getSender().sendMessage(packet);
	}
	
	private boolean activeSender(Server sender) {
		return (sender != null && sender.isChannelActive());
	}
	
	public void close() {
		service.shutdown();
		sender.disconnect();
		sender = null;
	}
}
